#include "texteditor.h"

#include <QDebug>

TextEditor::TextEditor(QString filename, QWidget *parent) :
    QPlainTextEdit(parent), filename_(filename), selectionStart_(0), selectionEnd_(0)
{
    updateLineNumberAreaWidth(0);

    setFrameShape(QFrame::NoFrame);
    setAttribute(Qt::WA_DeleteOnClose);
    setViewportMargins(lineNumberAreaWidth_,0,0,0);

    setFont(QFont("Monospace", 10));
    setTabStopWidth(4 * fontMetrics().width(QLatin1Char(' ')));
    setLineWrapMode(QPlainTextEdit::NoWrap);

    if(!filename_.isEmpty())
        openFile();

    lineNumberArea_ = new LineNumberArea(this);

    highlighter_ = new HtmlSyntaxHighlighter(this);
    highlighter_->setDocument(this->document());

    connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
    connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateCursorPosition()));
    connect(this, SIGNAL(selectionChanged()), this, SLOT(updateCursorSelection()));
}

bool TextEditor::saveFile()
{
    if(filename_.isEmpty()) {
        filename_ = QFileDialog::getSaveFileName(this, "Save file", QString(),
                       "HTML files (*.html *.htm);;Text files (*.txt)");
        if(filename_.isEmpty())
            return false;
    }

    QFile file(filename_);
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
        return false;
    file.write(toPlainText().toAscii());
    file.close();

    document()->setModified(false);
    emit saved();
    return true;
}

bool TextEditor::saveAs()
{
    QString fn = QFileDialog::getSaveFileName(this, "Save file as...", QString(),
                    "HTML files (*.html *.htm);;Text files (*.txt)");

    if(fn.isEmpty())
        return false;

    QFile file(fn);
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
        return false;
    file.write(toPlainText().toAscii());
    file.close();

    filename_ = fn;

    document()->setModified(false);
    emit saved();
    return true;
}

void TextEditor::openFile()
{
    QFile file(filename_);
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    setPlainText(file.readAll());
    file.close();
}

void TextEditor::paintLineNumberArea(QPaintEvent *event)
{
    QPainter painter(lineNumberArea_);
    painter.fillRect(event->rect(), QColor(235,235,235));

    QFont fntBold = painter.font();
    QFont fntNorm(fntBold);
    fntBold.setBold(true);

    QTextBlock block = firstVisibleBlock();
    int blockNumber = block.blockNumber();
    int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
    int bottom = top + (int) blockBoundingRect(block).height();

    painter.setPen(QColor(150,150,150));
    while (block.isValid() && top <= event->rect().bottom()) {
        if (block.isVisible() && bottom >= event->rect().top()) {
            QString number = QString::number(blockNumber + 1);
            if(blockNumber >= selectionStart_ && blockNumber <= selectionEnd_) {
                painter.setFont(fntBold);
                painter.setPen(QColor(75,75,75));
            } else {
                painter.setFont(fntNorm);
                painter.setPen(QColor(150,150,150));
            }
            painter.drawText(0, top, lineNumberAreaWidth_ - lineNumberArea_->spacing(), fontMetrics().height(),
                          Qt::AlignRight, number);
        }

        block = block.next();
        top = bottom;
        bottom = top + (int) blockBoundingRect(block).height();
        ++blockNumber;
    }
}

void TextEditor::resizeEvent(QResizeEvent *event)
{
    QPlainTextEdit::resizeEvent(event);

    QRect cr = contentsRect();
    lineNumberArea_->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth_, cr.height()));
}

void TextEditor::updateLineNumberArea(QRect rect, int dy)
{
    if(dy)
        lineNumberArea_->scroll(0, dy);
    else
        lineNumberArea_->update(0, rect.y(), lineNumberAreaWidth_, rect.height());

    if(rect.contains(viewport()->rect()))
        updateLineNumberAreaWidth(0);
}

void TextEditor::updateLineNumberAreaWidth(int blockcount)
{
    int digits = 0;
    while(blockcount > 0) {
        blockcount /= 10;
        digits++;
    }
    digits = qMax(digits, 3);

    lineNumberAreaWidth_ = digits * fontMetrics().width(QLatin1Char('9')) + lineNumberArea_->spacing() + 2;

    setViewportMargins(lineNumberAreaWidth_, 0, 0, 0);
}

void TextEditor::updateCursorPosition()
{
    if(!textCursor().hasSelection())
        selectionStart_ = selectionEnd_ = textCursor().blockNumber();
}

void TextEditor::updateCursorSelection()
{
    QTextCursor cursor = textCursor();
    QTextDocument * doc = document();

    selectionStart_ = doc->findBlock(cursor.position()).blockNumber();
    selectionEnd_ = doc->findBlock(cursor.anchor()).blockNumber();

    if(selectionStart_ > selectionEnd_)
        qSwap(selectionStart_, selectionEnd_);
}


LineNumberArea::LineNumberArea(TextEditor *parent) :
    QWidget(parent)
{
    editor_ = parent;
}

void LineNumberArea::paintEvent(QPaintEvent *event)
{
    editor_->paintLineNumberArea(event);
}
